package com.org.cardplayer.game;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * 
 * @author Thai Son
 * @Description Hand evaluation
 */
public class HandOrderConstant {
	public static int HIGH_CARD = 1;
	public static int ONE_PAIR = 2;
	public static int TWO_PAIR = 3;// done
	public static int THREE_OF_A_KIND = 4; // done
	public static int STRAIGHT = 5; // done
	public static int FLUSH = 6; // done
	public static int FULL_HOUSE = 7; // done
	public static int FOUR_OF_A_KIND = 8; // done
	public static int STRAIGHT_FLUSH = 9;
	public static int ROYAL_FLUSH = 10;

	public static Hand getBestOnePair(List<Card>[] value_map, List<Card> sorted) {
		List<Card> fullhand = null;

		for (int i = 13; i >= 1; i--) {
			List<Card> pair = value_map[i];
			if (pair != null) {
				if (pair.size() == 2) {
					fullhand = new ArrayList<Card>();
					fullhand.addAll(pair);
					int kicker_value = 0;
					// now find 3 kicker
					int kicker_count = 3;

					for (Card card : sorted) {
						if (card.getValue() != pair.get(0).getValue()) {
							fullhand.add(card);
							kicker_value += card.getValue();
							kicker_count--;
							if (kicker_count == 0)
								break;
						}
					}

					return new Hand(fullhand, HandOrderConstant.ONE_PAIR, pair
							.get(0).getValue() * 1000 + kicker_value);

				}
			}
		}
		return null;
	}

	public static Hand getBestTwoPair(List<Card>[] value_map, List<Card> sorted) {
		List<List<Card>> pairs = new ArrayList<List<Card>>();
		for (int i = 13; i >= 1; i--) {
			List<Card> list = value_map[i];
			if (list != null) {
				if (list.size() == 2) {
					pairs.add(list);
				}
			}
			// stop if get two pair
			if (pairs.size() == 2)
				break;
		}
		if (pairs.size() == 2) {
			List<Card> fullhand = new ArrayList<Card>();
			// now find the kicker

			for (Card card : sorted) {
				if (card.getValue() != pairs.get(0).get(0).getValue()
						&& card.getValue() != pairs.get(1).get(0).getValue()) {

					fullhand = new ArrayList<Card>();
					fullhand.addAll(pairs.get(0));
					fullhand.addAll(pairs.get(1));
					fullhand.add(card);
					return new Hand(fullhand, HandOrderConstant.TWO_PAIR, pairs
							.get(0).get(0).getValue()
							* 1000
							+ pairs.get(0).get(0).getValue()
							* 50
							+ card.getValue());
				}
			}
			// result = new Hand()
		}

		return null;
	}

	public static Hand getBestFullHouse(List<Card>[] value_map,
			List<Card> sorted) {
		Hand result = null;
		List<Card> triple = null;
		List<Card> pair = null;
		List<Card> fullhouse = null;
		boolean finded_tripple = false; // in case of two tripple
		boolean finded_pair = false;
		for (int i = 13; i >= 1; i--) {
			List<Card> list = value_map[i];
			if (list != null) {
				if (list.size() == 3) {
					if (!finded_tripple) {
						triple = list;
						finded_tripple = true;
					} else {     //two tripple
						pair = list;
						finded_pair = true;
					}
				} else if (list.size() == 2 && !finded_pair) {
					pair = list;
					finded_pair = true;
				}
			}
			// if finded pair and tripple then done loop
			if (finded_pair && finded_tripple)
				break;
		}
		if (pair != null && triple != null) {
			fullhouse = new ArrayList<Card>();
			fullhouse.addAll(triple);
			fullhouse.addAll(pair.subList(0, 2)); // in case of two tripple

			result = new Hand(fullhouse, HandOrderConstant.FULL_HOUSE, triple
					.get(0).getValue() * 1000 + pair.get(0).getValue());
		}
		return result;
	}

	// wrong kicker lol
	public static Hand getBestThreeKind(List<Card>[] value_map,
			List<Card> sorted) {
		Hand result = null;
		List<Card> fullhand = null;
		List<Card> tripple = null;
		if (sorted.size() >= 5) {
			for (int i = 13; i >= 1; i--) {
				List<Card> list = value_map[i];
				if (list != null) {
					if (list.size() == 3) {
						tripple = list;
						break;
					}
				}
			}
			if (tripple != null) {
				int kicker_count = 2;
				fullhand = new ArrayList<Card>();
				fullhand.addAll(tripple);
				int kicker_value = 0;
				for (Card card : sorted) {
					if (card.getValue() != tripple.get(0).getValue()) {
						// only get 2 kicker
						fullhand.add(card);
						kicker_value += card.getValue();
						kicker_count--;
						if (kicker_count == 0)
							break;
					}
				}
				result = new Hand(fullhand, HandOrderConstant.THREE_OF_A_KIND,
						fullhand.get(0).getValue() * 1000 + kicker_value);

			}
		}
		return result;
	}

	// wrong kicker lol
	public static Hand getBestFourKind(List<Card>[] value_map, List<Card> sorted) {
		List<Card> four = null;
		Card kicker = null;
		List<Card> fullhand = null;
		if (sorted.size() >= 5) {
			for (List<Card> list : value_map) {
				if (list != null) {
					if (list.size() == 4) {
						four = list;
						break;
					}
				}
			}
			if (four != null)
				for (Card card : sorted) {
					if (card.getValue() != four.get(0).getValue()) {
						kicker = card;
						fullhand = new ArrayList<Card>();
						fullhand.addAll(four);
						fullhand.add(kicker);
						return new Hand(fullhand,
								HandOrderConstant.FOUR_OF_A_KIND, four.get(0)
										.getValue() * 1000 + kicker.getValue());
					}
				}

		}

		return null;
	}

	/**
	 * Get Best Flush Hand
	 * 
	 * @param map
	 *            Map of all suited grouped
	 * @return
	 */
	public static Hand getBestFlushHand(HashMap<CardSuite, List<Card>> map) {
		// Now detect flush possible
		Hand result = null;
		for (CardSuite suite : CardSuite.values()) {
			if (map.containsKey(suite))
				if (map.get(suite).size() >= 5) {
					// detect straight possible
					List<Card> all_suited = map.get(suite);
					ListIterator<Card> itr = all_suited.listIterator();
					List<Card> best_flush = new ArrayList<Card>();
					for (int i = 0; i < 5; i++) {
						best_flush.add(itr.next());
					}
					result = new Hand(best_flush, FLUSH,
							HandOrderConstant.FLUSH * 1000
									+ best_flush.get(0).getValue());
					break;
				}
		}

		return result;
	}

	/**
	 * Sort Cards
	 * 
	 * @param cards
	 *            cards to sort
	 * @return
	 */

	public static List<Card> sortCard(Card[] cards) {
		List<Card> sorted = new ArrayList<Card>();
		for (Card card : cards) {
			if (sorted.isEmpty())
				sorted.add(card);
			else {
				Iterator<Card> itr = sorted.iterator();
				int i = 0;
				boolean added = false;
				while (itr.hasNext()) {
					if (card.getValue() > itr.next().getValue()) {
						sorted.add(i, card);
						added = true;
						break;
					}
					i++;
				}
				if (!added)
					sorted.add(i, card);
			}
		}
		return sorted;
	}

	/**
	 * Group a list of cards by suite
	 * 
	 * @param cards
	 * @return
	 */

	public static HashMap<CardSuite, List<Card>> groupCardBySuited(
			List<Card> cards) {
		HashMap<CardSuite, List<Card>> map = new HashMap<CardSuite, List<Card>>();
		for (Card card : cards) {
			if (map.containsKey(card.getSuite())) {
				map.get(card.getSuite()).add(card);
			} else {
				List<Card> list_card = new ArrayList<Card>();
				list_card.add(card);
				map.put(card.getSuite(), list_card);
			}
		}
		return map;
	}

	/**
	 * 
	 * @param cards
	 *            input is cards which is sorted
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<Card>[] groupCardByValue(List<Card> cards) {

		List<Card>[] map = (List<Card>[]) new List[15];
		for (Card card : cards) {
			if (map[card.getValue()] != null) {
				map[card.getValue()].add(card);
			} else {
				map[card.getValue()] = new ArrayList<Card>();
				map[card.getValue()].add(card);
			}
		}
		return map;
	}

	public static void printValueMap(List<Card>[] value_map) {
		for (int i = 1; i <= 13; i++) {
			System.out.print(";" + i + ":" + value_map[i]);
		}
	}

	/**
	 * 
	 * @param sorted_cards
	 *            : the input card list must be sorted in order to detect
	 *            straight
	 * @return the straight hand
	 */
	public static Hand getBestStraightHand(List<Card>[] value_map) {
		for (int i = 14; i >= 5; i--) {
			if (value_map[i] != null && value_map[i - 1] != null
					&& value_map[i - 2] != null && value_map[i - 3] != null
					&& value_map[i - 4] != null) {
				List<Card> straight = new ArrayList<Card>();
				straight.add(value_map[i].get(0));
				straight.add(value_map[i - 1].get(0));
				straight.add(value_map[i - 2].get(0));
				straight.add(value_map[i - 3].get(0));
				straight.add(value_map[i - 4].get(0));
				return new Hand(straight, HandOrderConstant.STRAIGHT,
						HandOrderConstant.STRAIGHT * 1000
								+ value_map[i].get(0).getValue());

			}
		}

		return null;
	}

	/**
	 * 
	 * @param cards
	 *            :cards to evaluation
	 * @return Hand strength
	 */
	public static int handEvaluation(Card[] cards) {
		// First sort it
		List<Card> sorted = HandOrderConstant.sortCard(cards);
		// Now group it by suited
		HashMap<CardSuite, List<Card>> map = HandOrderConstant
				.groupCardBySuited(sorted);
		List<Card>[] value_map = HandOrderConstant.groupCardByValue(sorted);
		// Start Evaluation

		// System.out.println(map);
		// System.out.println(sorted);
		System.out.println("best straight hand :");
		System.out.println(getBestStraightHand(value_map));
		System.out.println("best flush hand :");
		System.out.println(getBestFlushHand(map));
		System.out.println("best four of a kind hand");
		System.out.println(getBestFourKind(value_map, sorted));
		System.out.println("best full house hand");
		System.out.println(getBestFullHouse(value_map, sorted));
		System.out.println("best three of a kind hand");
		System.out.println(getBestThreeKind(value_map, sorted));
		System.out.println("best two pair hand");
		System.out.println(getBestTwoPair(value_map, sorted));
		System.out.println("best one pair hand");
		System.out.println(getBestOnePair(value_map, sorted));
		System.out.println("Sorted test all");
		System.out.println(sorted);
		System.out.println("Map test all");
		System.out.println(map);
		System.out.println("Value Map test all");
		printValueMap(value_map);
		return 0;
	}
}
